/**
 * @Author: 系统管理员
 * @Date: 2024-01-01
 * @Description: Koa日志中间件
 * @FilePath: \jmd-ser-ts\app\logger\loggerMiddleware.ts
 */

import { Context, Next } from 'koa';
import { Logger } from './Logger';

/**
 * Koa日志中间件
 * 记录HTTP请求和响应信息
 */
export const loggerMiddleware = async (ctx: Context, next: Next): Promise<void> => {
  const startTime = Date.now();
  const logger = Logger.http;
  const requestId = generateRequestId();
  
  // 存储请求ID到上下文，便于后续日志关联
  ctx.state.requestId = requestId;
  
  try {
    // 记录请求开始
    const requestInfo = Logger.getRequestLogInfo(ctx);
    logger.info(`[${requestId}] 请求开始`, {
      ...requestInfo,
      requestBody: sanitizeRequestBody(ctx.request.body),
    });
    
    // 执行下一个中间件
    await next();
    
    // 记录响应
    const responseTime = Date.now() - startTime;
    const responseInfo = {
      statusCode: ctx.status,
      responseTime: `${responseTime}ms`,
      responseSize: ctx.length || 0,
    };
    
    // 根据状态码使用不同的日志级别
    if (ctx.status < 400) {
      logger.info(`[${requestId}] 请求完成`, responseInfo);
    } else if (ctx.status < 500) {
      logger.warn(`[${requestId}] 请求完成(客户端错误)`, {
        ...responseInfo,
        error: (ctx.body as any)?.message || '客户端错误',
      });
    } else {
      logger.error(`[${requestId}] 请求完成(服务器错误)`, {
        ...responseInfo,
        error: (ctx.body as any)?.message || '服务器内部错误',
      });
    }
  } catch (error) {
    // 记录错误
    const responseTime = Date.now() - startTime;
    logger.error(`[${requestId}] 请求异常`, {
      error: {
        message: (error as Error).message,
        stack: process.env.NODE_ENV === 'development' ? (error as Error).stack : undefined,
      },
      responseTime: `${responseTime}ms`,
    });
    
    // 重新抛出错误，交给错误处理中间件处理
    throw error;
  }
};

/**
 * 生成唯一的请求ID
 * @returns 请求ID
 */
function generateRequestId(): string {
  return `${Date.now().toString(36)}-${Math.random().toString(36).substring(2, 9)}`;
}

/**
 * 清理请求体中的敏感信息
 * @param body 请求体
 * @returns 清理后的请求体
 */
function sanitizeRequestBody(body: any): any {
  if (!body || typeof body !== 'object') {
    return body;
  }
  
  const sanitized = { ...body };
  
  // 清理常见的敏感字段
  const sensitiveFields = ['password', 'token', 'secret', 'key', 'creditCard', 'ssn'];
  sensitiveFields.forEach(field => {
    if (sanitized[field]) {
      sanitized[field] = '******';
    }
  });
  
  // 递归清理嵌套对象
  Object.keys(sanitized).forEach(key => {
    if (typeof sanitized[key] === 'object' && sanitized[key] !== null) {
      sanitized[key] = sanitizeRequestBody(sanitized[key]);
    }
  });
  
  return sanitized;
}

/**
 * 记录Koa上下文相关的错误日志
 * @param ctx Koa上下文
 * @param error 错误对象
 * @param message 自定义错误消息
 */
export const logContextError = (ctx: Context, error: Error, message?: string): void => {
  const logger = Logger.error;
  const requestId = ctx.state.requestId || generateRequestId();
  
  logger.error(message || '上下文错误', {
    requestId,
    request: Logger.getRequestLogInfo(ctx),
    error: {
      message: error.message,
      stack: process.env.NODE_ENV === 'development' ? error.stack : undefined,
      name: error.name,
    },
    timestamp: new Date().toISOString(),
  });
};